/**
 * 关于使用HyForm组件时，所用到的类型
 *
 * 我们有7大组件类型
 * 其中每一个组件都单独暴露出来一个接口
 * 每个接口必传inputType字段
 * 输入inputType后,typescript将自动判断其剩下的组件属性
 *
 *
 * @name: HyFormInterface
 * @data: 2024/4/12 14:59
 */
import type {FormRules, UploadFile, FormInstance, UploadRequestOptions} from 'element-plus'
import type {Component, ReactiveEffect} from "vue";
import type {Method} from "axios";
// @ts-ignore
import {UploadFiles, UploadUserFile} from "element-plus";

/**
 * 全局表单中确定按钮的回调定义
 */
interface SubmitBtnCallBackFunInterface {
  (formParamsObj: any ,formEl: FormInstance):any
}

/**
 * 整个form表单的配置
 * <p>FormParamsInterface泛型 表单类型,与后端对应<p>
 */
export default interface HyFormInterface {
  /** 表单主体 */
  formModelList: Array<HyInputFormItemInterface | HyDateFormItemInterface | HySelectFormInterface | HyElUploadInterface | HyCustomInterface>,
  /** el-form-item的长度(标签的长度) */
  labelWidth?: number,
  /** 表单中每个具体组件的宽度 */
  itemWidth?: number,
  /** 星号显示的位置 */
  requireAsteriskPosition?: "left" | "right",
  /** 行内表单模式,默认为false */
  inline?: boolean,
  /** 表单域标签的位置 */
  labelPosition?: 'left' | 'right' | 'top',
  /** 是否显示校验错误信息 */
  showMessage?: boolean,
  /** 是否以行内形式展示校验信息 */
  inlineMessage?: boolean,
  /** 整体表单的大小 */
  size?: 'large' | 'default' | 'small',
  /** 当校验失败时,滚动到第一个错误表单项 */
  scrollToError?: boolean,
  /** 提交方式 */
  method?: Method,
  /** 提交地址,为空则不进行发送请求 */
  url?: string,
  /** 请求的data数据,!!!注意: 表单的数据和data会合并发送给后端 */
  data?: any,
  /** 请求的params数据 */
  params?: any,
  /** 请求的路径上的数据 */
  query?: Array<number | string>,
  /** 提交按钮文本,默认文本为"提交" */
  submitBtnTitle?: string,
  /** 取消按钮文本,默认文本为"重置" */
  cancelBtnTitle?: string,
  /** 提交按钮的回调方法,
   * 如果传入了回调方法,
   * 那么url,data,params和query失效
   * 回调方法中,会给你传入两个参数,一个是表单的数据对象
   * 一个是表单的ref对象
   */
  requestCallback?:  SubmitBtnCallBackFunInterface,
  /** 请求之后的回调(已经获取数据), 注意,只有使用url才有回调,否则无效 */
  responseCallback?: (result: any) => void,
  /** 返回按钮的回调方法,返回按钮的回调方法如果不传入,
   * 默认为重置表单
   * 如果传入了回调方法,那么不会再执行重置表单
   * 回调方法中,会给你传入两个参数,即
   * 表单对象和表单对象的ref
   */
  cancelBtnCallBackFun?: SubmitBtnCallBackFunInterface,
  /** 是否只读控制全局组件, 默认为false, 每个item也有自己的只读属性 */
  isReadonly?: boolean
}


/**
 * 其实最好的状态时根据用户输入的inputType
 * 然后动态指定不同的属性,让使用者清晰明了的知道这个输入框类型有哪些字段
 *
 * 目前我们想到的有两种方案
 * 1. 再封装一层,然后使用泛型
 *  优点: 目前可以实现, 简单, 数据结构清晰明了
 *  缺点: 在封装时,就要在加上一层 .属性 不方便
 *  如果我们使用了泛型,那么,每一个item都要是一个对象,
 *  然后在使用form时,又要去将每一个对象拼成数组太不方便了
 *
 */

/**
 * @description 表单项的公共属性
 * 关于泛型后期我们再选
 */
export interface HyFormItemPublicInterface {
  inputType: "input" | "date" | "select" | "upload"  | "custom" | "tree",
  /** 匹配字段,发送给后端 */
  field: string,
  /** 表单对象 */
  form?: Object,
  /** 大小,默认为default */
  size?: 'large' | 'default' | 'small',
  /** 原生id属性 */
  id?: string,
  /** 标签展示标题 */
  label?: string,
  /** 单独标签的宽度 */
  labelWidth?:string,
  /** 为输入时提示信息 */
  placeholder?: string,
  /** 是否显示清空按钮 */
  clearable?: boolean,
  /** 组件样式 */
  inputStyle?: Object
  /**
   * form中的属性,如果传入该字段,我们将从后端传过来的JSON数据取出
   * 注意,如果传入字段,请传入url等属性,否则不进行赋值
   * 如果此字段的json数据为null或者undefined,这依然会继续赋值
   * */
  name?: string,
  /**
   * @description 关于value和url
   * url的优先级更高,
   * 如果传入了url,那么我们将会请求接口
   */
  value?: any,
  /** 数据校验规则,跟element-plus相同,具体属性不再赘述 */
  rule?: Array<
    { required?: boolean, message?: string, trigger?: "blur" | "change" } |
    { min?: number, max?: number, message?: string, trigger?: "blur" | "change" }
  >,
  /**
   * 注意,这里传入的只是每一个formItem,所以传入的不是对象
   * 是否可用,
   * 在一些场景下,有些字段可以修改,有些不能修改,此时
   * 该属性就可以派上用场了
   */
  isDisabled?: boolean,
  /** 是否显示清除按钮 */
  isClearable?: boolean,
  /**
   * 关于只读,这里我们不再使用 element-plus 提供的,而是直接将结果
   * 转为elFormItem标签 + span标签进行展示
   */
  isReadonly?: boolean,
  onChange?: Function,
}


/**
 * @description 表单项的网络请求属性
 *
 */
export interface HyFormItemNetWorkPublicInterface<ParamsType,DataType> {
  /** 请求地址 */
  url?: string,
  /** 请求方式 */
  method?: Method,
  /** 请求头部设置 */
  headers?:Object,
  /** 请求的params,类型与泛型P对应 */
  params?: ParamsType | any,
  /** 请求的data,类型与泛型D对应 */
  data?: any,
  /** 请求之间的回调(未获取数据) */
  requestCallback?: Function,
  /** 请求之后的回调(已经获取数据) */
  responseCallback?: Function,
}


/**
 * @description 自定义框架HyElInput中可以接收的参数
 * @param P  params的泛型
 * @param D data的泛型
 */
export interface HyInputFormItemInterface extends HyFormItemPublicInterface{
  /** 是否为密码输入 */
  showPassword?: boolean,
  showWordLimit?: boolean,
  suffixIcon?: any,
  prefixIcon?: any,
  autofocus?: boolean,
  /**
   * 注意,这里传入的只是每一个formItem,所以传入的不是对象
   * 是否可用,
   * 在一些场景下,有些字段可以修改,有些不能修改,此时
   * 该属性就可以派上用场了
   */
  /**
   * 关于只读,这里我们不再使用 element-plus 提供的,而是直接将结果
   * 转为elFormItem标签 + span标签进行展示
   */
  // isReadonly?: boolean,
  modelValue?: any,
  maxlength?: number | string,
  minlength?: number | string,
  resize?: 'none' | 'both' | 'horizontal' | 'vertical',
  autosize?: boolean,
  validateEvent?: boolean,
  /** 获取焦点时回调 */
  onFocus?: Function,
  /** 失去焦点时的回调 */
  onBlur?: Function,
  // onChange?: Function,
  click?: Function,
  regExpCallback?: Function,
  //请求之间的回调(未获取数据)
  requestCallback?: Function,
  /**
   * 请求之后的回调(已经获取数据)
   */
  responseCallback?: Function,
}

/**
 * @description 自定义框架HyElDate中可以接收的参数
 * 关于泛型,目前暂时不做
 * 等到后期如果有需求再加
 */
export interface HyDateFormItemInterface extends HyFormItemPublicInterface{
  /** 组件类型 */
  inputType: "date",
  /** 日期选择框的显示类型 */
  type?: 'year' | 'years' | 'month' | 'date' | 'dates' | 'datetime' | 'week' | 'datetimerange' | 'daterange' | 'monthrange'
  /** 日期框显示格式化 */
  dateFormat?: string,
  /** 时间显示格式化 */
  timeFormat?: string,
  /** 日期框值格式化 */
  valueFormat?: any,
  /** 选择范围时的分隔符 */
  rangeSeparator?: string,
  /** 范围选择时开始日期的占位内容 */
  startPlaceholder?:string,
  /** 范围选择时结束日期的占位内容 */
  endPlaceholder?:string
  /** 如果选择一个日期范围,则可以通过来制定start字段 */
  startDateField?:string,
  /** 如果选择一个日期范围,则可以通过来制定end字段 */
  endDateField?:string,
  /**
   * 关于回调函数,其实这里我们一直很纠结,对于匿名函数,
   * 到底应该使用箭头函数
   * 还是应该使用普通的function函数
   * 其实对于这个问题,没有必要过分纠结,
   * 我们只需要记住,箭头函数没有this指向,她会自动寻找上下文作为this
   * 而普通函数,这是谁调用,谁就是this
   * 例如在使用axios调用接口时,使用箭头函数有this,而使用普通函数没有this
   * 所以这里应根据实际开发场景去选择,
   * 在一般情况下,使用哪种函数还是根据自己的习惯来决定
   */
  isUnlinkPanels?: boolean,
  prefixIcon?:string,
  clearIcon?:string
  shortcuts?:Array<{text: string, value: Date | Function}>,
  isValidateEvent?:boolean
}

/**
 * @description 自定义框架HyElSelect中可以接收的参数
 *
 */
export interface HySelectFormInterface extends HyFormItemPublicInterface, HyFormItemNetWorkPublicInterface<any,any>{
  inputType: "select",
  /** 是否支持多选 */
  isMultiple?:boolean,
  /** 是否支持筛选 */
  isFilterable?:boolean,
  /** 从服务器加载数据时显示的文本，默认为“Loading” */
  loadingText?: string,
  /** 多选场景下用户最多可以选择的项目数 */
  multipleLimit?: number,
  /** Select 输入框的原生 autocomplete 属性 */
  autocomplete?: string,
  /** 作为 value 唯一标识的键名，绑定值为对象类型时必填 */
  valueKey?:string,
  /** 多选时是否将选中值按文字的形式展示 */
  isCollapseTags?: boolean,
  /** 当鼠标悬停于折叠标签的文本时，是否显示所有选中的标签 */
  collapseTagsTooltip?: boolean,
  /** 下拉列表子项列表 */
  optionList:Array<any>,
  /** 下拉列表子项的值(value)字段 */
  optionValueField?:string,
  /** 下拉列表子项的显示(label)字段 */
  optionLabelField?:string,
  /** 下拉列表子项的唯一(key)字段,如果没有传,则默认使用index */
  optionKeyField?: string,
  /** 自定义筛选方法 */
  filterMethod?: Function,
  /** 下拉框出现/隐藏时触发 */
  visibleChange?: Function,
}

/**
 * @description 自定义框架HyElUpload类型
 */
export interface HyElUploadInterface extends HyFormItemPublicInterface, HyFormItemNetWorkPublicInterface<any, any>{
  /** 上传类型 */
  inputType:"upload",
  /**
   * 使用上传接口时传递给后端的字段,
   * 也就是element-plus中的name属性
   */
  uploadField?:string,
  /** 是否支持多选文件 */
  multiple?:boolean,
  /** 是否支持发送cookie凭证信息 */
  withCredentials?:boolean,
  /** 是否显示已上传文件列表 */
  showFileList?:boolean,
  /** 是否启用拖拽上传 */
  drag?:boolean,
  /** 点击文件列表中已上传的文件时的钩子 */
  onPreview?:(uploadFile: UploadFile) => void,
  /** 文件列表移除文件时的钩子 */
  onRemove?:Function,
  /** 文件上传成功时的钩子 */
  onSuccess?:Function,
  /** 文件上传失败时的钩子 */
  onError?:Function,
  /** 文件上传时的钩子 */
  onProgress?: Function,
  /** 文件状态改变时的钩子，添加文件、上传成功和上传失败时都会被调用 */
  onChange?: (uploadFile: UploadFile, uploadFiles: UploadFiles) => void,
  /** 当超出限制时，执行的钩子函数 */
  onExceed?: Function,
  /** 上传文件之前的钩子，参数为上传的文件， 若返回false或者返回 Promise 且被 reject，则停止上传。 */
  beforeUpload?: Function,
  /** 删除文件之前的钩子，参数为上传的文件和文件列表， 若返回 false 或者返回 Promise 且被 reject，则停止删除。 */
  beforeRemove?: Function,
  /** 默认上传文件 */
  fileList?:Array<UploadUserFile | any>,
  /** 覆盖默认的 Xhr 行为，允许自行实现上传文件的请求 */
  httpRequest?: (options: UploadRequestOptions) => XMLHttpRequest | Promise<unknown>,
  /** 允许上传文件的最大数量 */
  limit?: number,
  /** 自定义default模板 */
  customComponent?:Component,
  /** 上传文件时最底部的文件上传提示信息 */
  promptInformation?: string,
  /** 上传按钮的文本 */
  uploadBtnTitle?: string,
  /** 拼接字符串的符号,
   * <p>注意,如果传入,其name属性传入的值,取出后
   * 将以字符串+拼接符号的方式赋值<p>
   */
  splicingSymbol?: string,
  /** 是否启用下载,默认为false */
  isDownloadFile?: boolean,
  /** 启用下载后,下载路径的字段,当然也可以使用回调函数 */
  fileUrlNameField?: string,
}

/**
 * @description 自定义框架HyCustom类型
 * 用户自定义组件显示
 */
// @ts-ignore
export interface HyCustomInterface extends  HyFormItemPublicInterface{
  inputType:"custom",
  component?: Component,
}

export interface HyElTreeInterface extends HyFormItemPublicInterface,HyFormItemNetWorkPublicInterface<any, any>{
  /** 树形选择器 */
  inputType:"tree",
  /** 默认数据 */
  data: any,
  /** 展示的字段，默认为 label */
  labelField?: string,
  /** 值的字段?: 默认为value */
  valueField?: string,
  /** 子级的字段 */
  childrenField?: string,
  /** 是否开启异步加载，异步开启后，data将无效 */
  lazy?: boolean,

}
